﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using SSG.Tools;

namespace SSG.Model
{
    /// <summary>
    /// Some common methods
    /// </summary>
    public abstract class BaseEntity
    {
        public const string CanonicalNameSeperator = ".";
        public const char CanonicalNameSeperatorChar = '.';

        /// <summary>
        /// Name
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// No space in name
        /// </summary>
        [XmlIgnore]
        public string NameNoSpaces
        {
            get
            {
                return String.IsNullOrEmpty(this.Name) ? this.Name : this.Name.Replace(" ", String.Empty);
            }
        }

        /// <summary>
        /// No space in name, replaced by "_"
        /// </summary>
        [XmlIgnore]
        public string NameSpaceToUnderscore
        {
            get
            {
                return String.IsNullOrEmpty(this.Name) ? this.Name : this.Name.Replace(' ', '_');
            }
        }

        /// <summary>
        /// Full name, e.g. SID + Approach, Runway + Airport
        /// </summary>
        public string CanonicalName { get; set; }

        /// <summary>
        /// Parts of canonical name
        /// </summary>
        public string[] CanonicalNameParts { get { return String.IsNullOrEmpty(this.CanonicalName) ? null : this.CanonicalName.Split(CanonicalNameSeperatorChar); } }

        /// <summary>
        /// Description
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// Raw format
        /// </summary>
        public string DataRawString { get; set; }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="canonicalName"></param>
        /// <param name="description"></param>
        protected BaseEntity(string name, string canonicalName = null, string description = null)
        {
            this.Name = name;
            this.CanonicalName = String.IsNullOrEmpty(canonicalName) ? name : canonicalName;
            this.Description = description;
        }

        /// <summary>
        /// Meaningful representation
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return string.IsNullOrEmpty(this.Name) ? base.ToString() : this.GetClassname() + " " + this.Name;
        }

        /// <summary>
        /// Find entity given by name.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static T FindByName<T>(string name, List<T> entities) where T : BaseEntity
        {
            if (String.IsNullOrEmpty(name)) return null;
            T entity = entities.NullSafeIsEmpty() ? null : entities.FirstOrDefault(e => name.NullSafeEqualsIgnoreCase(e.Name));
            return entity;
        }

        /// <summary>
        /// Name replaced by settings rules
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        public string GetNameReplacedByRules(GeneratorSettings settings)
        {
            if (settings == null) return this.Name;
            if (settings.NdbNameCorrection) {
                if (this.Name.EndsWith("NB")) {
                    // Am I a navaid?
                    Navaid navaid = this as Navaid;
                    if (navaid != null) return (navaid.Type == Navaid.NavaidType.Ndb) ? navaid.Name.TrimEnd("NB") : navaid.Name;

                    // Is there a NDB with the replaced NDB
                    string ndb = this.Name.TrimEnd("NB");
                    navaid = Navaid.Navaids.FirstOrDefault(nav => nav.Type == Navaid.NavaidType.Ndb && nav.Name.Equals(ndb, StringComparison.InvariantCultureIgnoreCase));
                    return navaid != null ? navaid.Name : this.Name;
                }
            }
            return this.Name;
        }

        /// <summary>
        /// All names as one string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static string GetNamesString<T>(List<T> entities) where T : BaseEntity
        {
            if (entities.NullSafeIsEmpty()) return "";
            StringBuilder sb = new StringBuilder();
            foreach (T baseEntity in entities) {
                sb.AppendWithSeperator(baseEntity.Name, ", ");
            }
            return sb.ToString();
        }

        /// <summary>
        /// List will only contain unique names, no duplicates
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static List<T> UniqueNameList<T>(List<T> entities) where T : BaseEntity
        {
            if (entities.NullSafeIsEmpty()) return new List<T>();
            List<T> newList = new List<T>();
            List<string> names = new List<string>();
            foreach (T baseEntity in entities) {
                if (names.Contains(baseEntity.Name)) continue;
                names.Add(baseEntity.Name);
                newList.Add(baseEntity);
            }
            return newList;
        }

        /// <summary>
        /// Names of all entities.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static List<string> Names<T>(List<T> entities) where T : BaseEntity
        {
            return entities.NullSafeIsEmpty() ? new List<string>() : entities.Select(baseEntity => baseEntity.Name).ToList();
        }
    }
}